package com.cn.encryp;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

/**
 * @description: RSA非对称加密算法
 * @Author 朱明川
 * @Date 2023/7/20 11:22
 * @Version 1.0
 *
 * todo RSA 【Rivest-Shamir Adelman算法（ RSA）】和DSA【数字签名算法（DSA）】 对比
 * RSA 与 DSA 都是非对称加密算法。其中RSA的安全性是基于极其困难的大整数的分解（两个素数的乘积）；DSA 的安全性是基于整数有限域离散对数难题。
 * 基本上可以认为相同密钥长度的 RSA 算法与 DSA 算法安全性相当。
 * 有点要注意，RSA 的安全性依赖于大数分解，但是否等同于大数分解一直未能得到理论上的证明，因为没有证明破解 RSA 就一定需要作大数分解。
 * 不过也不必太过担心，RSA 从诞生以来，经历了各种攻击，至今未被完全攻破（依靠暴力破解，小于1024位密钥长度的 RSA 有被攻破的记录，但未从算法上被攻破）。
 * todo 用途：
 * DSA 只能用于数字签名，而无法用于加密（某些扩展可以支持加密）；
 * RSA 即可作为数字签名，也可以作为加密算法。不过作为加密使用的 RSA 有着随密钥长度增加，性能急剧下降的问题。
 * todo 性能：
 * 相同密钥长度下，DSA 做签名时速度更快，但做签名验证时速度较慢，一般情况验证签名的次数多于签名的次数。
 * 相同密钥长度下，DSA （在扩展支持下）解密密文更快，而加密更慢；
 * RSA 正好反过来，一般来说解密次数多于加密次数。不过由于非对称加密算法的先天性能问题，两者都不是加密的好选择。
 * todo 业界支持：
 * 在业界支持方面，RSA 显然是赢家。RSA 具有更为广泛的部署与支持。
 */
public class RSA {
    private static final String RSA_ALGORITHM = "RSA";

    /**
     * 生成RSA密钥对
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        // 密钥大小为2048位
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 使用公钥加密数据
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 使用私钥解密数据
     */
    public static String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = cipher.doFinal(decodedData);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        System.out.println("公钥：" +publicKey);
        PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("私钥：" + privateKey.toString());
        String originalString = "Java学习 + 面试指南：javaguide.cn";
        String encryptedData = encrypt(originalString, publicKey);
        String decryptedData = decrypt(encryptedData, privateKey);
        System.out.println("Original String: " + originalString);
        System.out.println("RSA Encrypted Data : " + encryptedData);
        System.out.println("RSA Decrypted Data : " + decryptedData);
    }

}
